/******************************************************************************

	COPYRIGHT(C) JONAS 'SORTIE' TERMANSEN 2010.

	This file is part of Maxsi Engine.

	Maxsi Engine is free software: you can redistribute it and/or modify it
	under the terms of the GNU Lesser General Public License as published by
	the Free Software Foundation, either version 3 of the License, or (at your
	option) any later version.

	Maxsi Engine is distributed in the hope that it will be useful, but WITHOUT
	ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
	FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
	for more details.

	You should have received a copy of the GNU Lesser General Public License
	along with Maxsi Engine. If not, see <http://www.gnu.org/licenses/>.

	MaxsiEngine.dll
	A general purpose C++ library for the Maxsi Technology Project.

	MaxsiInteger.cpp
	Useful functions for dealing with integers.

******************************************************************************/

#include "MaxsiEngine.h"

BeginMaxsiNamespace

size_t IntLength10(int Input) { size_t Result = (Input > 0) ? 1 : 2; while ( Input >= 10 ) { Input /= 10; Result++; } return Result; }
size_t Int8Length10(int8_t Input) { size_t Result = (Input > 0) ? 1 : 2; while ( Input >= 10 ) { Input /= 10; Result++; } return Result; }
size_t Int16Length10(int16_t Input) { size_t Result = (Input > 0) ? 1 : 2; while ( Input >= 10 ) { Input /= 10; Result++; } return Result; }
size_t Int32Length10(int32_t Input) { size_t Result = (Input > 0) ? 1 : 2; while ( Input >= 10 ) { Input /= 10; Result++; } return Result; }
size_t Int64Length10(int64_t Input) { size_t Result = (Input > 0) ? 1 : 2; while ( Input >= 10 ) { Input /= 10; Result++; } return Result; }

size_t UIntLength10(unsigned int Input) { size_t Result = 1; while ( Input >= 10 ) { Input /= 10; Result++; } return Result; }
size_t UInt8Length10(uint8_t Input) { size_t Result = 1; while ( Input >= 10 ) { Input /= 10; Result++; } return Result; }
size_t UInt16Length10(uint16_t Input) { size_t Result = 1; while ( Input >= 10 ) { Input /= 10; Result++; } return Result; }
size_t UInt32Length10(uint32_t Input) { size_t Result = 1; while ( Input >= 10 ) { Input /= 10; Result++; } return Result; }
size_t UInt64Length10(uint64_t Input) { size_t Result = 1; while ( Input >= 10 ) { Input /= 10; Result++; } return Result; }

size_t UIntLength16(int Input) { size_t Result = 1; while ( Input >= 16 ) { Input /= 16; Result++; } return Result; }
size_t UInt8Length16(uint8_t Input) { size_t Result = 1; while ( Input >= 16 ) { Input /= 16; Result++; } return Result; }
size_t UInt16Length16(uint16_t Input) { size_t Result = 1; while ( Input >= 16 ) { Input /= 16; Result++; } return Result; }
size_t UInt32Length16(uint32_t Input) { size_t Result = 1; while ( Input >= 16 ) { Input /= 16; Result++; } return Result; }
size_t UInt64Length16(uint64_t Input) { size_t Result = 1; while ( Input >= 16 ) { Input /= 16; Result++; } return Result; }

size_t SizeLength10(size_t Input) { size_t Result = 1; while ( Input >= 10 ) { Input /= 10; Result++; } return Result; }
size_t SizeLength16(size_t Input) { size_t Result = 1; while ( Input >= 16 ) { Input /= 16; Result++; } return Result; }

size_t PrintUIntA10(char* Dest, unsigned int Input) { size_t Result = UIntLength10(Input); size_t O = Result; while ( O ) { Dest[O-1] = '0' + (Input % 10); O--; Input /= 10; } return Result; }
size_t PrintUInt8A10(char* Dest, uint8_t Input) { size_t Result = UInt8Length10(Input); size_t O = Result; while ( O ) { Dest[O-1] = '0' + (Input % 10); O--; Input /= 10; } return Result; }
size_t PrintUInt16A10(char* Dest, uint16_t Input) { size_t Result = UInt16Length10(Input); size_t O = Result; while ( O ) { Dest[O-1] = '0' + (Input % 10); O--; Input /= 10; } return Result; }
size_t PrintUInt32A10(char* Dest, uint32_t Input) { size_t Result = UInt32Length10(Input); size_t O = Result; while ( O ) { Dest[O-1] = '0' + (Input % 10); O--; Input /= 10; } return Result; }
size_t PrintUInt64A10(char* Dest, uint64_t Input) { size_t Result = UInt64Length10(Input); size_t O = Result; while ( O ) { Dest[O-1] = '0' + (Input % 10); O--; Input /= 10; } return Result; }

size_t PrintUIntA16X(char* Dest, unsigned int Input) { size_t Result = UIntLength16(Input); size_t O = Result; while ( O ) { Dest[O-1] = ((Input % 16 > 9) ? 'A' + (Input % 16) - 10: '0' + (Input % 16)); O--; Input /= 16; } return Result; }
size_t PrintUInt8A16X(char* Dest, uint8_t Input) { size_t Result = UInt8Length16(Input); size_t O = Result; while ( O ) { Dest[O-1] = ((Input % 16 > 9) ? 'A' + (Input % 16) - 10: '0' + (Input % 16)); O--; Input /= 16; } return Result; }
size_t PrintUInt16A16X(char* Dest, uint16_t Input) { size_t Result = UInt16Length16(Input); size_t O = Result; while ( O ) { Dest[O-1] = ((Input % 16 > 9) ? 'A' + (Input % 16) - 10: '0' + (Input % 16)); O--; Input /= 16; } return Result; }
size_t PrintUInt32A16X(char* Dest, uint32_t Input) { size_t Result = UInt32Length16(Input); size_t O = Result; while ( O ) { Dest[O-1] = ((Input % 16 > 9) ? 'A' + (Input % 16) - 10: '0' + (Input % 16)); O--; Input /= 16; } return Result; }
size_t PrintUInt64A16X(char* Dest, uint64_t Input) { size_t Result = UInt64Length16(Input); size_t O = Result; while ( O ) { Dest[O-1] = ((Input % 16 > 9) ? 'A' + (Input % 16) - 10: '0' + (Input % 16)); O--; Input /= 16; } return Result; }

size_t PrintSizeA10(char* Dest, size_t Input) { size_t Result = SizeLength10(Input); size_t O = Result; while ( O ) { Dest[O-1] = '0' + (Input % 10); O--; Input /= 10; } return Result; }
size_t PrintSizeA16X(char* Dest, size_t Input) { size_t Result = SizeLength16(Input); size_t O = Result; while ( O ) { Dest[O-1] = ((Input % 16 > 9) ? 'A' + (Input % 16) - 10: '0' + (Input % 16)); O--; Input /= 16; } return Result; }

size_t TestPrintStringALength(char* Format, size_t FormatLen, va_list Arguments)
{
	size_t Result = 0;

	for ( size_t I = 0; I < FormatLen; I++ )
	{
		size_t Left = FormatLen - I;

		if ( Format[I] == '%' )	
		{
			//=============================================================================
			//	%[Type]s - A string!
			//=============================================================================
			MAXSI_TODO_LOW("These routines may allocate up to 4 times the required memory to do the job!");
			if ( Left > 1 && Format[I+1] == 's') { Result += sizeof(MESTR)*MESTRLEN(va_arg(Arguments, MESTR*)); } else
			if ( Left > 2 && Format[I+1] == 'U' && Format[I+2] == 's') { Result += strlen((char*)va_arg(Arguments, UTF8_t*)); } else
			if ( Left > 2 && Format[I+1] == 'W' && Format[I+2] == 's') { Result += sizeof(wchar_t)*wcslen((wchar_t*)va_arg(Arguments, wchar_t*)); } else
			if ( Left > 2 && Format[I+1] == 'A' && Format[I+2] == 's') { Result += strlen(va_arg(Arguments, char*)); } else

			//=============================================================================
			//	%[Width]u - unsigned integer.
			//=============================================================================
			if ( Left > 1 && Format[I+1] == 'u' ) { Result += UIntLength10(va_arg(Arguments, unsigned int)); I += 1;} else
			if ( Left > 2 && Format[I+1] == '8' && Format[I+2] == 'u' ) { Result += UInt8Length10((uint8_t)va_arg(Arguments, unsigned int)); I += 2;} else
			if ( Left > 3 && Format[I+1] == '1' && Format[I+2] == '6' && Format[I+3] == 'u' ) { Result += UInt16Length10((uint16_t)va_arg(Arguments, unsigned int)); I += 1;} else
			if ( Left > 3 && Format[I+1] == '3' && Format[I+2] == '2' && Format[I+3] == 'u' ) { Result += UInt32Length10(va_arg(Arguments, uint32_t)); I += 3;} else
			if ( Left > 3 && Format[I+1] == '6' && Format[I+2] == '4' && Format[I+3] == 'u' ) { Result += UInt64Length10(va_arg(Arguments, uint64_t)); I += 3;} else

			//=============================================================================
			//	%[Width]X - unsigned integer in upper case hex form.
			//=============================================================================
			if ( Left > 1 && Format[I+1] == 'X' ) { Result += UIntLength16(va_arg(Arguments, unsigned int)); I += 1;} else
			if ( Left > 2 && Format[I+1] == '8' && Format[I+2] == 'X' ) { Result += UInt8Length16((uint8_t)va_arg(Arguments, unsigned int)); I += 2;} else
			if ( Left > 3 && Format[I+1] == '1' && Format[I+2] == '6' && Format[I+3] == 'X' ) { Result += UInt16Length16((uint16_t)va_arg(Arguments, unsigned int)); I += 3;} else
			if ( Left > 3 && Format[I+1] == '3' && Format[I+2] == '2' && Format[I+3] == 'X' ) { Result += UInt32Length16(va_arg(Arguments, uint32_t)); I += 3;} else
			if ( Left > 3 && Format[I+1] == '6' && Format[I+2] == '4' && Format[I+3] == 'X' ) { Result += UInt64Length16(va_arg(Arguments, uint64_t)); I += 3;} else

			//=============================================================================
			//	%z - size_t.
			//=============================================================================
			if ( Left > 1 && Format[I+1] == 'z' ) { Result += SizeLength10(va_arg(Arguments, size_t)); } else
			if ( Left > 2 && Format[I+1] == 'X' && Format[I+2] == 'z' ) { Result += SizeLength16(va_arg(Arguments, size_t)); }
		}
		//=============================================================================
		//	Just a normal character.
		//=============================================================================
		else
		{
			Result++;
		}
	}

	return Result;
}

size_t TestPrintStringAActual(char* Format, char* Dest, size_t FormatLen, va_list Arguments)
{
	size_t	O				=	0;
	char*	Input			=	NULL;
	size_t	InputLength		=	0;
	bool	DeleteInput		=	false;

	for ( size_t I = 0; I < FormatLen; I++ )
	{
		size_t Left = FormatLen - I;

		if ( Format[I] == '%' )	
		{
			//=============================================================================
			//	%[Type]s - A string!
			//=============================================================================
			if ( Left > 1 && Format[I+1] == 's')
			{
#ifdef Maxsi_Using_Unicode_Internally
				Input = MESTR2CHAR(va_arg(Arguments, MESTR*)); if ( Input ) { InputLength = strlen(Input); DeleteInput = true; I+=1; }
#else
				Input = va_arg(Arguments, MESTR*); InputSize = sizeof(MESTR)*strlen(Input); I+=1;
#endif
			}
			else if ( Left > 2 && Format[I+1] == 'U' && Format[I+2] == 's')
			{
				Input = UTF82CHAR(va_arg(Arguments, UTF8_t*)); if ( Input ) { InputLength = strlen(Input); DeleteInput = true; I+=2; }
			}
			else if ( Left > 2 && Format[I+1] == 'W' && Format[I+2] == 's')
			{
				Input = WCHAR2CHAR(va_arg(Arguments, wchar_t*)); if ( Input ) { InputLength = strlen(Input); DeleteInput = true; I+=2; }
			}
			else if ( Left > 2 && Format[I+1] == 'A' && Format[I+2] == 's')
			{
				Input = va_arg(Arguments, char*); InputLength = strlen(Input); I+=2;			
			}

			if ( Input ) { memcpy(Dest+O, Input, sizeof(char)*InputLength); O+=InputLength; if ( DeleteInput ) { delete[] Input; DeleteInput = false; } Input = NULL; }

			//=============================================================================
			//	%[Width]u - unsigned integer.
			//=============================================================================
			if ( Left > 1 && Format[I+1] == 'u' ) { O += PrintUIntA10(Dest + O, va_arg(Arguments, unsigned int)); I += 1; } else
			if ( Left > 2 && Format[I+1] == '8' && Format[I+2] == 'u' ) { O += PrintUInt8A10(Dest + O, (uint8_t)va_arg(Arguments, unsigned int)); I += 2; } else
			if ( Left > 3 && Format[I+1] == '1' && Format[I+2] == '6' && Format[I+3] == 'u' ) { O += PrintUInt16A10(Dest + O, (uint16_t)va_arg(Arguments, unsigned int)); I += 3; } else
			if ( Left > 3 && Format[I+1] == '3' && Format[I+2] == '2' && Format[I+3] == 'u' ) { O += PrintUInt32A10(Dest + O, va_arg(Arguments, uint32_t)); I += 3; } else
			if ( Left > 3 && Format[I+1] == '6' && Format[I+2] == '4' && Format[I+3] == 'u' ) { O += PrintUInt64A10(Dest + O, va_arg(Arguments, uint64_t)); I += 3; } else

			//=============================================================================
			//	%[Width]X - unsigned integer in upper case hex form.
			//=============================================================================
			if ( Left > 1 && Format[I+1] == 'X' ) { O += PrintUIntA16X(Dest + O, va_arg(Arguments, unsigned int)); I += 1; } else
			if ( Left > 2 && Format[I+1] == '8' && Format[I+2] == 'X' ) { O += PrintUInt8A16X(Dest + O, (uint8_t)va_arg(Arguments, unsigned int)); I += 2; } else
			if ( Left > 3 && Format[I+1] == '1' && Format[I+2] == '6' && Format[I+3] == 'X' ) { O += PrintUInt16A16X(Dest + O, (uint16_t)va_arg(Arguments, unsigned int)); I += 3; } else
			if ( Left > 3 && Format[I+1] == '3' && Format[I+2] == '2' && Format[I+3] == 'X' ) { O += PrintUInt32A16X(Dest + O, va_arg(Arguments, uint32_t)); I += 3; } else
			if ( Left > 3 && Format[I+1] == '6' && Format[I+2] == '4' && Format[I+3] == 'X' ) { O += PrintUInt64A16X(Dest + O, va_arg(Arguments, uint64_t)); I += 3; } else

			//=============================================================================
			//	%z - size_t.
			//=============================================================================
			if ( Left > 1 && Format[I+1] == 'z' ) { O += PrintSizeA10(Dest + O, va_arg(Arguments, size_t)); I += 1; } else
			if ( Left > 2 && Format[I+1] == 'X' && Format[I+2] == 'z' ) { O += PrintSizeA16X(Dest + O, va_arg(Arguments, size_t)); I += 1; }
		}
		//=============================================================================
		//	Just a normal character.
		//=============================================================================
		else
		{
			Dest[O] = Format[I]; O++;
		}		
	}

	// NUL-terminate the string.
	Dest[O] = 0;	

	return O;
}

char* TestPrintStringA(char* Format, ...)
{
	size_t FormatLen = strlen(Format);

	va_list Arguments;

	va_start(Arguments, Format);

	size_t ResultLength = TestPrintStringALength(Format, FormatLen, Arguments);

	va_end(Arguments);

	char* Result = new char[ResultLength + 1];

	if ( Result == NULL ) { return NULL; }

	va_start(Arguments, Format);

	TestPrintStringAActual(Format, Result, FormatLen, Arguments);

	va_end(Arguments);	

	return Result;
}

EndMaxsiNamespace

